<?php
/**
 * Created by PhpStorm.
 * Author: Len
 * mail: i@91coder.org
 * Date: 2017/5/30
 * Time: 20:44
 * 聊天室 已聊天室为单位建立用户信息
 */
date_default_timezone_set('PRC');
ini_set('memory_limit', '50M');

class swooleChat {

    public $map = array();
    public $userList = array();
    public $redis;

    const SPACE_MARK = '_{}_';
    const MAX_USER_NUM = 100;
    const SWOOLECHAT_USER_ROOM_KEY = 'swooleChat:user:%s:room:%d';

    const SYSTEM_ICON = 'http://wwww.libenfu.com/head/smile.gif';
    const FIGHTING_JS = 'http://wwww.libenfu.com/game/4/js/js.js';
	const CHAT_ICON = 'http://wwww.libenfu.com/head/%d.jpg';

    const CHAT_MESSAGE = "<div class='talk_recordbox%s clear'><div class='user'><img src='%s' height='90px' width='90px' />%s</div><div class='talk_recordtextbg'>&nbsp;</div><div class='talk_recordtext'><h3>%s</h3><span class='talk_time'>%s</span></div></div>";

	private function __construct($config) 
	{
        $this->initService($config);
	}

    /**
     * 运行脚本
     * @param $config
     */
    public static function run($config)
	{
		return new self($config);
	}

    /**
     * 初始化服务
     * @param $config
     */
    private function initService($config)
    {
        try {
            $websocket_config = $config['websocket'];
            $swoole_config = $config['swoole'];
            $redis_config = $config['redis'];

            $redis = new redis();
            $redis->connect($redis_config['host'], $redis_config['port']);
            $redis_config['auth'] && $redis->auth($redis_config['auth']);
            $this->redis = $redis;

            $server = new swoole_websocket_server($websocket_config['host'], $websocket_config['port'], SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL);
            $server->set($swoole_config);
            $server->on('open', array($this, 'onOpen'));
            $server->on('message', array($this, 'onMessage'));
            $server->on('close', array($this, 'onClose'));
            $server->start();
        } catch (\Exception $exception) {
            exit('初始化服务失败');
        }
    }

    /**
     * 监听打开连接
     * @param swoole_websocket_server $server
     * @param $request
     */
    public function onOpen(swoole_websocket_server $server, $request)
    {
        if (self::MAX_USER_NUM < count($this->map)) {
            return $server->push($request->fd, "<script>alert('本房间人数已满')</script>");
        }

        $this->map[$request->fd] = $request->fd;
    }

    /**
     * 监听消息请求
     * @param swoole_websocket_server $server
     * @param $frame
     */
    public function onMessage(swoole_websocket_server $server, $frame)
    {
        //请求数据
        $data = $frame->data;
        $fd = $frame->fd;
        list($user_key, $msg) = explode(self::SPACE_MARK, $data);

        $msg = htmlentities($msg);
        $cur_num = count($this->map);
        $user_info = $this->getUserInfo($fd, $user_key);

        #判断客户端数据
        switch ($msg) {
            case '__md9501__': # 通知所有用户谁进入聊天室
                $this->oneSend($server, $fd, array(
                    'class' => '',
                    'icon' => self::SYSTEM_ICON,
                    'user_name' => '系统',
                    'char_msg' => "欢迎您 : {$user_info['user_name']} 来到SwooleChat聊天室 ",
                    'char_date' => "当前聊天室人数 : {$cur_num}"
                ));

                $this->batchSend($server, $fd, array(
                    'class' => '',
                    'icon' => self::SYSTEM_ICON,
                    'user_name' => '系统',
                    'char_msg' => " {$user_info['user_name']} - 进入SwooleChat聊天室",
                    'char_date' => "当前聊天室人数 : {$cur_num}"
                ));
                break;
            case '__md001__': #返回当前所有用户名称
                $nameStr = implode(' , ', array_column($this->userList, 'user_name'));
                $this->oneSend($server, $fd, array(
                    'class' => '',
                    'icon' => self::SYSTEM_ICON,
                    'user_name' => '系统',
                    'char_msg' => "当前在线用户 : {$nameStr}",
                    'char_date' => "当前聊天室人数 : {$cur_num}"
                ));
                break;
            case 'md502': #让所有用户断开连接
                $this->closeTotal($server, $fd);
                break;
            case '俄罗斯方块':
                $this->tetrisSend($server);
                break;
            case '打架呀':
                $this->fightingSend($server);
                break;
            default:
                $cur_time = date('Y-m-d H:i:s');
                $this->oneSend($server, $fd, array(
                    'class' => 'me',
                    'icon' => $user_info['icon'],
                    'user_name' => $user_info['user_name'],
                    'char_msg' => $msg,
                    'char_date' => $cur_time
                ));

                $this->batchSend($server, $fd, array(
                    'class' => '',
                    'icon' => $user_info['icon'],
                    'user_name' => $user_info['user_name'],
                    'char_msg' => $msg,
                    'char_date' => $cur_time
                ));
        }
    }

    /**
     * 推送格斗游戏
     * @param swoole_websocket_server $server
     */
    public function fightingSend(swoole_websocket_server $server)
    {
        $req = '<div class="ken stance"></div>
                <script src="'. self::FIGHTING_JS.'"></script>';
        foreach($this->map as $fd) {
            $server->push($fd, $req);
        }
    }

    /**
     * 推送俄罗斯方块
     * @param swoole_websocket_server $server
     */
    public function tetrisSend(swoole_websocket_server $server)
    {
        $req = '<div class="game" style="width:500px; height:630px; margin:0 auto;">
                </div>
                <script>
                    $(".game").blockrain({speed: 10,theme: "vim"});
                </script>';
        foreach($this->map as $fd) {
            $server->push($fd, $req);
        }
    }

    /**
     * 关闭所有连接
     * @param swoole_websocket_server $server
     * @param $cur_fd
     * @return mixed
     */
    public function closeTotal(swoole_websocket_server $server, $cur_fd)
    {
        foreach($this->map as $fd) {
            if($cur_fd == $fd) continue;
            $server->close($fd, true);
            $req = '<script>alert("Forced to disconnect!")</script>';
            return $server->push($cur_fd, $req);
        }
    }

    /**
     * 发送消息给单个用户
     * @param swoole_websocket_server $server
     * @param $cur_fd
     * @param $chat_data
     * @return mixed
     */
    public function oneSend(swoole_websocket_server $server, $cur_fd, $chat_data)
    {
        $req = vsprintf(self::CHAT_MESSAGE, $chat_data);

        return $server->push($cur_fd, $req);
    }

    /**
     * 批量发送消息
     * @param $server
     * @param $cur_fd
     * @param $chat_data
     * @return mixed
     */
    public function batchSend(swoole_websocket_server $server, $cur_fd, $chat_data)
    {
        foreach($this->map as $fd) {
            if ($fd == $cur_fd) continue;
            $user_info = $this->getUserInfo($fd);
            $chat_data['icon'] = sprintf($chat_data['icon'], $user_info['icon']);
            $chat_data['char_msg'] = sprintf($chat_data['char_msg'], $user_info['user_name']);
            $chat_data['user_name'] = sprintf($chat_data['user_name'], $user_info['user_name']);
            $response = vsprintf(self::CHAT_MESSAGE, $chat_data);
            $server->push($fd, $response);
        }

        return;
    }

    /**
     * 错误返回处理
     * @param swoole_websocket_server $server
     * @param $cur_fd
     * @return mixed
     */
    public function errorHandling(swoole_websocket_server $server, $cur_fd)
    {
        $req = "<script>window.location.href='http://wwww.libenfu.com'</script>";

        return $server->push($cur_fd, $req);
    }

    /**
     * 获取用户信息key
     * @param $user_key
     * @param $room_id
     * @return string
     */
    public function getUserRoomKey($user_key, $room_id = 1)
    {
        return sprintf(self::SWOOLECHAT_USER_ROOM_KEY, $user_key, $room_id);
    }

    /**
     * 获取用户信息
     * @param $fd
     * @param null $user_key
     * @return mixed
     */
    public function getUserInfo($fd, $user_key = null)
    {
        if(empty($this->userList[$fd]) && !empty($user_key)) {
        	$redis_key = $this->getUserRoomKey($user_key);
        	$user_info = array(
            	'fd' => $fd,
            	'last_time' => date('Y-m-d H:i:s'),
				'icon' => sprintf(self::CHAT_ICON, rand(0, 9))
        	);
        	$this->redis->hmset($redis_key, $user_info);
        	$this->userList[$fd] = $this->redis->hgetall($redis_key);
        }

		return $this->userList[$fd];
    }

    /**
     * 监听退出连接
     * @param swoole_websocket_server $server
     * @param $cur_fd
     */
    public function onClose(swoole_websocket_server $server, $cur_fd)
    {
        unset($this->map[$cur_fd]);
        $this->batchSend($server, $cur_fd, array(
            'class' => '',
            'icon' => self::SYSTEM_ICON,
            'user_name' => $this->userList[$cur_fd]['user_name'],
            'char_msg' => $this->userList[$cur_fd]['user_name'] . ' 退出聊天室',
            'char_date' => date('Y-m-d H:i:s')
        ));
        unset($this->userList[$cur_fd]);
    }
}

define('CONFIG_FILE', __DIR__ . '/config.ini');

if (!file_exists(CONFIG_FILE)) {
    exit('缺少配置文件');
}
$config = parse_ini_file(CONFIG_FILE, true);

swooleChat::run($config);
